home *** CD-ROM | disk | FTP | other *** search
/ Aminet 31 / Aminet 31 (1999)(Schatztruhe)[!][Jun 1999].iso / Aminet / dev / cross / z88dk_v1.0s.lha / src / cpp / cpp1.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  16.8 KB  |  497 lines

  1. /*
  2.  * CPP main program.
  3.  *
  4.  * Edit history
  5.  * 21-May-84    MM    "Field test" release
  6.  * 23-May-84    MM    Some minor hacks.
  7.  * 30-May-84    ARF    Didn't get enough memory for __DATE__
  8.  *            Added code to read stdin if no input
  9.  *            files are provided.
  10.  * 29-Jun-84    MM    Added ARF's suggestions, Unixifying cpp.
  11.  * 11-Jul-84    MM    "Official" first release (that's what I thought!)
  12.  * 22-Jul-84    MM/ARF/SCK Fixed line number bugs, added cpp recognition
  13.  *            of #line, fixed problems with #include.
  14.  * 23-Jul-84    MM    More (minor) include hacking, some documentation.
  15.  *            Also, redid cpp's #include files
  16.  * 25-Jul-84    MM    #line filename isn't used for #include searchlist
  17.  *            #line format is <number> <optional name>
  18.  * 25-Jul-84    ARF/MM    Various bugs, mostly serious.  Removed homemade doprint
  19.  * 01-Aug-84    MM    Fixed recursion bug, remove extra newlines and
  20.  *            leading whitespace from cpp output.
  21.  * 02-Aug-84    MM    Hacked (i.e. optimized) out blank lines and unneeded
  22.  *            whitespace in general.  Cleaned up unget()'s.
  23.  * 03-Aug-84    Keie    Several bug fixes from Ed Keizer, Vrije Universitet.
  24.  *            -- corrected arg. count in -D and pre-defined
  25.  *            macros.  Also, allow \n inside macro actual parameter
  26.  *            lists.
  27.  * 06-Aug-84    MM    If debugging, dump the preset vector at startup.
  28.  * 12-Aug-84    MM/SCK    Some small changes from Sam Kendall
  29.  * 15-Aug-84    Keie/MM    cerror, cwarn, etc. take a single string arg.
  30.  *            cierror, etc. take a single int. arg.
  31.  *            changed LINE_PREFIX slightly so it can be
  32.  *            changed in the makefile.
  33.  * 31-Aug-84    MM    USENET net.sources release.
  34.  *  7-Sep-84    SCH/ado Lint complaints
  35.  * 10-Sep-84    Keie    Char's can't be signed in some implementations
  36.  * 11-Sep-84    ado    Added -C flag, pathological line number fix
  37.  * 13-Sep-84    ado    Added -E flag (does nothing) and "-" file for stdin.
  38.  * 14-Sep-84    MM    Allow # 123 as a synonym for #line 123
  39.  * 19-Sep-84    MM    scanid always reads to token, make sure #line is
  40.  *            written to a new line, even if -C switch given.
  41.  *            Also, cpp - - reads stdin, writes stdout.
  42.  * 03-Oct-84    ado/MM    Several changes to line counting and keepcomments
  43.  *            stuff.  Also a rewritten control() hasher -- much
  44.  *            simpler and no less "perfect". Note also changes
  45.  *            in cpp3.c to fix numeric scanning.
  46.  * 04-Oct-84    MM    Added recognition of macro formal parameters if
  47.  *            they are the only thing in a string, per the
  48.  *            draft standard.
  49.  * 08-Oct-84    MM    One more attack on scannumber
  50.  * 15-Oct-84    MM/ado    Added -N to disable predefined symbols.  Fixed
  51.  *            linecount if COMMENT_INVISIBLE enabled.
  52.  * 22-Oct-84    MM    Don't evaluate the #if/#ifdef argument if
  53.  *            compilation is supressed.  This prevents
  54.  *            unnecessary error messages in sequences such as
  55.  *                #ifdef FOO        -- undefined
  56.  *                #if FOO == 10    -- shouldn't print warning
  57.  * 25-Oct-84    MM    Fixed bug in false ifdef supression.  On vms,
  58.  *            #include <foo> should open foo.h -- this duplicates
  59.  *            the behavior of Vax-C
  60.  * 31-Oct-84    ado/MM    Parametized $ in indentifiers.  Added a better
  61.  *            token concatenator and took out the trial
  62.  *            concatenation code.  Also improved #ifdef code
  63.  *            and cleaned up the macro recursion tester.
  64.  *  2-Nov-84    MM/ado    Some bug fixes in token concatenation, also
  65.  *            a variety of minor (uninteresting) hacks.
  66.  *  6-Nov-84    MM    Happy Birthday.  Broke into 4 files and added
  67.  *            #if sizeof (basic_types)
  68.  *  9-Nov-84    MM    Added -S* for pointer type sizes
  69.  * 13-Nov-84    MM    Split cpp1.c, added vms defaulting
  70.  * 23-Nov-84    MM/ado    -E supresses error exit, added CPP_INCLUDE,
  71.  *            fixed strncpy bug.
  72.  *  3-Dec-84    ado/MM    Added OLD_PREPROCESSOR
  73.  *  7-Dec-84    MM    Stuff in Nov 12 Draft Standard
  74.  * 17-Dec-84    george    Fixed problems with recursive macros
  75.  * 17-Dec-84    MM    Yet another attack on #if's (f/t)level removed.
  76.  * 07-Jan-85    ado    Init defines before doing command line options
  77.  *            so -Uunix works.
  78.  * 11-Feb-94    MH      Changed token concatenation (hopefully) to ANSI 
  79.  *                      standard and added token quoting.
  80.  * 26-Nov-94    MH      -P problem of outputting a newline after leading
  81.  *                      white space in order to correct counter if fixed 
  82.  *                      now.  Several other fixes for -P, too.
  83.  */
  84.  
  85. #include    <stdio.h>
  86. #include    <ctype.h>
  87. #include    "cppdef.h"
  88. #include    "cpp.h"
  89.  
  90. /*
  91.  * Commonly used global variables:
  92.  * line        is the current input line number.
  93.  * wrongline    is set in many places when the actual output
  94.  *        line is out of sync with the numbering, e.g,
  95.  *        when expanding a macro with an embedded newline.
  96.  *
  97.  * token    holds the last identifier scanned (which might
  98.  *        be a candidate for macro expansion).
  99.  * errors    is the running cpp error counter.
  100.  * infile    is the head of a linked list of input files (extended by
  101.  *        #include and macros being expanded).  infile always points
  102.  *        to the current file/macro.  infile->parent to the includer,
  103.  *        etc.  infile->fd is NULL if this input stream is a macro.
  104.  */
  105. int        line;            /* Current line number        */
  106. int        wrongline;        /* Force #line to compiler    */
  107. char        token[IDMAX + 1];    /* Current input token        */
  108. int        errors;            /* cpp error counter        */
  109. FILEINFO    *infile = NULL;        /* Current input file        */
  110. #if DEBUG
  111. int        debug;            /* TRUE if debugging now    */
  112. #endif
  113. /*
  114.  * This counter is incremented when a macro expansion is initiated.
  115.  * If it exceeds a built-in value, the expansion stops -- this tests
  116.  * for a runaway condition:
  117.  *    #define X Y
  118.  *    #define Y X
  119.  *    X
  120.  * This can be disabled by falsifying rec_recover.  (Nothing does this
  121.  * currently: it is a hook for an eventual invocation flag.)
  122.  */
  123. int        recursion;        /* Infinite recursion counter    */
  124. int        rec_recover = TRUE;    /* Unwind recursive macros    */
  125.  
  126. /*
  127.  * instring is set TRUE when a string is scanned.  It modifies the
  128.  * behavior of the "get next character" routine, causing all characters
  129.  * to be passed to the caller (except <DEF_MAGIC>).  Note especially that
  130.  * comments and \<newline> are not removed from the source.  (This
  131.  * prevents cpp output lines from being arbitrarily long).
  132.  *
  133.  * inmacro is set by #define -- it absorbs comments and converts
  134.  * form-feed and vertical-tab to space, but returns \<newline>
  135.  * to the caller.  Strictly speaking, this is a bug as \<newline>
  136.  * shouldn't delimit tokens, but we'll worry about that some other
  137.  * time -- it is more important to prevent infinitly long output lines.
  138.  *
  139.  * instring and inmarcor are parameters to the get() routine which
  140.  * were made global for speed.
  141.  */
  142. int        instring = FALSE;    /* TRUE if scanning string    */
  143. int        inmacro = FALSE;    /* TRUE if #defining a macro    */
  144.  
  145. /*
  146.  * work[] and workp are used to store one piece of text in a temporay
  147.  * buffer.  To initialize storage, set workp = work.  To store one
  148.  * character, call save(c);  (This will fatally exit if there isn't
  149.  * room.)  To terminate the string, call save(EOS).  Note that
  150.  * the work buffer is used by several subroutines -- be sure your
  151.  * data won't be overwritten.  The extra byte in the allocation is
  152.  * needed for string formal replacement.
  153.  */
  154. char        work[NWORK + 1];    /* Work buffer            */
  155. char        *workp;            /* Work buffer pointer        */
  156.  
  157. /*
  158.  * keepcomments is set TRUE by the -C option.  If TRUE, comments
  159.  * are written directly to the output stream.  This is needed if
  160.  * the output from cpp is to be passed to lint (which uses commands
  161.  * embedded in comments).  cflag contains the permanent state of the
  162.  * -C flag.  keepcomments is always falsified when processing #control
  163.  * commands and when compilation is supressed by a false #if
  164.  *
  165.  * If eflag is set, CPP returns "success" even if non-fatal errors
  166.  * were detected.
  167.  *
  168.  * If nflag is non-zero, no symbols are predefined except __LINE__.
  169.  * __FILE__, and __DATE__.  If nflag > 1, absolutely no symbols
  170.  * are predefined.
  171.  */
  172. int        keepcomments = FALSE;    /* Write out comments flag    */
  173. int        cflag = FALSE;        /* -C option (keep comments)    */
  174. int        eflag = FALSE;        /* -E option (never fail)    */
  175. int        nflag = 0;        /* -N option (no predefines)    */
  176. int        pflag = 0;        /* -P option (no #line)        */
  177.  
  178. /*
  179.  * ifstack[] holds information about nested #if's.  It is always
  180.  * accessed via *ifptr.  The information is as follows:
  181.  *    WAS_COMPILING    state of compiling flag at outer level.
  182.  *    ELSE_SEEN    set TRUE when #else seen to prevent 2nd #else.
  183.  *    TRUE_SEEN    set TRUE when #if or #elif succeeds
  184.  * ifstack[0] holds the compiling flag.  It is TRUE if compilation
  185.  * is currently enabled.  Note that this must be initialized TRUE.
  186.  */
  187. char        ifstack[BLK_NEST] = { TRUE };    /* #if information    */
  188. char        *ifptr = ifstack;        /* -> current ifstack[] */
  189.  
  190. /*
  191.  * incdir[] stores the -i directories (and the system-specific
  192.  * #include <...> directories.
  193.  */
  194. char    *incdir[NINCLUDE];        /* -i directories        */
  195. char    **incend = incdir;        /* -> free space in incdir[]    */
  196.  
  197. /*
  198.  * This is the table used to predefine target machine and operating
  199.  * system designators.  It may need hacking for specific circumstances.
  200.  * Note: it is not clear that this is part of the Ansi Standard.
  201.  * The -N option supresses preset definitions.
  202.  */
  203. char    *preset[] = {            /* names defined at cpp start    */
  204. #ifdef    MACHINE
  205.     MACHINE,
  206. #endif
  207. #ifdef    SYSTEM
  208.     SYSTEM,
  209. #endif
  210. #ifdef    COMPILER
  211.     COMPILER,
  212. #endif
  213. #if    DEBUG
  214.     "decus_cpp",            /* Ourselves!            */
  215. #endif
  216.     NULL                /* Must be last            */
  217. };
  218.  
  219. /*
  220.  * The value of these predefined symbols must be recomputed whenever
  221.  * they are evaluated.  The order must not be changed.
  222.  */
  223. char    *magic[] = {            /* Note: order is important    */
  224.     "__LINE__",
  225.     "__FILE__",
  226.     NULL                /* Must be last            */
  227. };
  228.  
  229. main(argc, argv)
  230. int        argc;
  231. char        *argv[];
  232. {
  233.     register int    i;
  234.  
  235. #if HOST == SYS_VMS
  236.     argc = getredirection(argc, argv);    /* vms >file and <file    */
  237. #endif
  238.     initdefines();                /* O.S. specific def's    */
  239.     i = dooptions(argc, argv);        /* Command line -flags    */
  240.     switch (i) {
  241.     case 3:
  242.         /*
  243.          * Get output file, "-" means use stdout.
  244.          */
  245.         if (!streq(argv[2], "-")) {
  246. #if HOST == SYS_VMS
  247.         /*
  248.          * On vms, reopen stdout with "vanilla rms" attributes.
  249.          */
  250.         if ((i = creat(argv[2], 0, "rat=cr", "rfm=var")) == -1
  251.          || dup2(i, fileno(stdout)) == -1) {
  252. #else
  253.         if (freopen(argv[2], "w", stdout) == NULL) {
  254. #endif
  255.             perror(argv[2]);
  256.             cerror("Can't open output file `%s'", argv[2]);
  257.             exit(IO_ERROR);
  258.         }
  259.         }                /* Continue by opening input    */
  260.     case 2:                /* One file -> stdin        */
  261.         /*
  262.          * Open input file, "-" means use stdin.
  263.          */
  264.         if (!streq(argv[1], "-")) {
  265.         if (freopen(argv[1], "r", stdin) == NULL) {
  266.             perror(argv[1]);
  267.             cerror("Can't open input file `%s'", argv[1]);
  268.             exit(IO_ERROR);
  269.         }
  270.         strcpy(work, argv[1]);    /* Remember input filename    */
  271.         break;
  272.         }                /* Else, just get stdin        */
  273.     case 0:                /* No args?            */
  274.     case 1:                /* No files, stdin -> stdout    */
  275. #if HOST == SYS_UNIX
  276.         work[0] = EOS;        /* Unix can't find stdin name    */
  277. #else
  278.         fgetname(stdin, work);    /* Vax-11C, Decus C know name    */
  279. #endif
  280.         break;
  281.  
  282.     default:
  283.         exit(IO_ERROR);        /* Can't happen            */
  284.     }
  285.     setincdirs();            /* Setup -I include directories    */
  286.     addfile(stdin, work);        /* "open" main input file    */
  287. #if DEBUG
  288.     if (debug > 0)
  289.         dumpdef("preset #define symbols");
  290. #endif
  291.     cppmain();            /* Process main file        */
  292.     if ((i = (ifptr - &ifstack[0])) != 0) {
  293. #if OLD_PREPROCESSOR
  294.         ciwarn("Inside #ifdef block at end of input, depth = %d", i);
  295. #else
  296.         cierror("Inside #ifdef block at end of input, depth = %d", i);
  297. #endif
  298.     }
  299.     fclose(stdout);
  300.     if (errors > 0) {
  301.         fprintf(stderr, (errors == 1)
  302.         ? "%d error in preprocessor\n"
  303.         : "%d errors in preprocessor\n", errors);
  304.         if (!eflag)
  305.         exit(IO_ERROR);
  306.     }
  307.     exit(IO_NORMAL);        /* No errors or -E option set    */
  308. }
  309.  
  310. FILE_LOCAL
  311. cppmain()
  312. /*
  313.  * Main process for cpp -- copies tokens from the current input
  314.  * stream (main file, include file, or a macro) to the output
  315.  * file.
  316.  */
  317. {
  318.     register int        c;        /* Current character    */
  319.     register int        counter;    /* newlines and spaces    */
  320.     extern int        output();    /* Output one character    */
  321.  
  322.     /*
  323.      * Explicitly output a #line at the start of cpp output so
  324.      * that lint (etc.) knows the name of the original source
  325.      * file.  If we don't do this explicitly, we may get
  326.      * the name of the first #include file instead.
  327.      * We also seem to need a blank line following that first #line.
  328.      */
  329.     sharp();
  330.     output('\n');
  331.     /*
  332.      * This loop is started "from the top" at the beginning of each line
  333.      * wrongline is set TRUE in many places if it is necessary to write
  334.      * a #line record.  (But we don't write them when expanding macros.)
  335.      *
  336.      * The counter variable has two different uses:  at
  337.      * the start of a line, it counts the number of blank lines that
  338.      * have been skipped over.  These are then either output via
  339.      * #line records or by outputting explicit blank lines.
  340.       * When expanding tokens within a line, the counter remembers
  341.      * whether a blank/tab has been output.  These are dropped
  342.      * at the end of the line, and replaced by a single blank
  343.      * within lines.
  344.      */
  345.     for (;;) {
  346.         counter = 0;            /* Count empty lines    */
  347.         for (;;) {                /* For each line, ...    */
  348.         while (type[(c = get())] == SPA)/* Skip leading blanks    */
  349.             if (pflag && compiling)    /* in this line.    */
  350.             output(c);
  351.         if (c == '\n')            /* If line's all blank,    */
  352.             if (pflag)            /* Do nothing now    */
  353.             output('\n');
  354.             else ++counter;
  355.         else if (c == '#') {        /* Is 1st non-space '#'    */
  356.             keepcomments = FALSE;    /* Don't pass comments    */
  357.             counter = control(counter);    /* Yes, do a #command    */
  358.             if (pflag)            /* Output missing \n    */
  359.             while (counter>0) { output('\n'); --counter; }
  360.             keepcomments = (cflag && compiling);
  361.         }
  362.         else if (c == EOF_CHAR)        /* At end of file?    */
  363.             break;
  364.         else if (!compiling) {        /* #ifdef false?    */
  365.             skipnl();            /* Skip to newline    */
  366.             if (pflag) 
  367.             output('\n');        /* Output blank line    */
  368.             else counter++;        /* Count it, too.    */
  369.         }
  370.         else {
  371.             break;            /* Actual token        */
  372.         }
  373.         }
  374.         if (c == EOF_CHAR)            /* Exit process at    */
  375.         break;                /* End of file        */
  376.         /*
  377.          * If the loop didn't terminate because of end of file, we
  378.          * know there is a token to compile.  First, clean up after
  379.          * absorbing newlines.  counter has the number we skipped.
  380.          */
  381.         if ((wrongline && infile->fp != NULL) || counter > 4)
  382.         sharp();            /* Output # line number    */
  383.         else {                /* If just a few, stuff    */
  384.         while (--counter >= 0)        /* them out ourselves    */
  385.             output('\n');
  386.         }
  387.         /*
  388.          * Process each token on this line.
  389.          */
  390.         unget();                /* Reread the char.    */
  391.         for (;;) {                /* For the whole line,    */
  392.         do {                /* Token concat. loop    */
  393.             for (counter = 0; (type[(c = get())] == SPA);) {
  394.             if (pflag) output(c);
  395.             else {
  396. #if COMMENT_INVISIBLE
  397.                 if (c != COM_SEP)
  398.                     counter++;
  399. #else
  400.                 counter++;        /* Skip over blanks    */
  401. #endif
  402.             }
  403.             }
  404.             if (c == EOF_CHAR || c == '\n')
  405.             goto end_line;        /* Exit line loop    */
  406.             else if (counter > 0)    /* If we got any spaces    */
  407.             output(' ');        /* Output one space    */
  408.             c = macroid(c);        /* Grab the token    */
  409.         } while (type[c] == LET && catenate());
  410.         if (c == EOF_CHAR || c == '\n')    /* From macro exp error    */
  411.             goto end_line;        /* Exit line loop    */
  412.         switch (type[c]) {
  413.         case LET:
  414.             output(*token); fputs(token+1, stdout);    /* Quite ordinary token    */
  415.             break;
  416.  
  417.  
  418.         case DIG:            /* Output a number    */
  419.         case DOT:            /* Dot may begin floats    */
  420.             scannumber(c, output);
  421.             break;
  422.  
  423.         case QUO:            /* char or string const    */
  424.             scanstring(c, output);    /* Copy it to output    */
  425.             break;
  426.  
  427.         default:            /* Some other character    */
  428.             output(c);            /* Just output it    */
  429.             break;
  430.         }                /* Switch ends        */
  431.         }                    /* Line for loop    */
  432. end_line:   if (c == '\n') {            /* Compiling at EOL?    */
  433.         output('\n');            /* Output newline, if    */
  434.         if (infile->fp == NULL)        /* Expanding a macro,    */
  435.             wrongline = TRUE;        /* Output # line later    */
  436.         }
  437.     }                    /* Continue until EOF    */
  438. }
  439.  
  440. int join=0;
  441.  
  442. output(c)
  443. int        c;
  444. /*
  445.  * Output one character to stdout -- output() is passed as an
  446.  * argument to scanstring()
  447.  */
  448. {
  449.      static quote_quote=0;
  450.      
  451.     if (c == DBL_QUOTE)
  452.         { if (join) { putchar('\\'); join=0; } putchar('"'); quote_quote=!quote_quote; }
  453.     else if (c == '"')
  454.         { if (join) { putchar('\\'); join=0; } if (quote_quote) putchar('\\'); putchar('"'); }
  455.     else if (c == '\\')
  456.         { if (join) { putchar('\\'); putchar('\\'); join=0; } else join=1; }
  457.     else if (c=='\n')
  458.         { if (join==0) putchar('\n'); else wrongline=1; join=0; }
  459.         else
  460. #if COMMENT_INVISIBLE
  461.     if (c != TOK_SEP && c != COM_SEP)
  462. #else
  463.     if (c != TOK_SEP)
  464. #endif
  465.         { if (join) putchar('\\'); join=0; putchar(c); }
  466. }
  467.  
  468. static char    *sharpfilename = NULL;
  469.  
  470. FILE_LOCAL
  471. sharp()
  472. /*
  473.  * Output a line number line.
  474.  */
  475. {
  476.     register char        *name;
  477.  
  478.     if (keepcomments)            /* Make sure # comes on    */
  479.         putchar('\n');            /* a fresh, new line.    */
  480.     if (pflag == 0) {
  481.         printf("#%s %d", LINE_PREFIX, line);
  482.         if (infile->fp != NULL) {
  483.             name = (infile->progname != NULL)
  484.             ? infile->progname : infile->filename;
  485.             if (sharpfilename == NULL
  486.             || sharpfilename != NULL && !streq(name, sharpfilename)) {
  487.             if (sharpfilename != NULL)
  488.                 free(sharpfilename);
  489.             sharpfilename = savestring(name);
  490.             output(' '); output('"'); fputs(name,stdout); output('"');
  491.             }
  492.         }
  493.         output('\n');
  494.     }
  495.     wrongline = FALSE;
  496. }
  497.